package net.airtheva.uniqueTask4;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import net.airtheva.uniqueTask4.view.FloatLyricView;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.View;

import com.mpatric.mp3agic.ID3v1;
import com.mpatric.mp3agic.ID3v2;
import com.mpatric.mp3agic.InvalidDataException;
import com.mpatric.mp3agic.Mp3File;
import com.mpatric.mp3agic.NotSupportedException;
import com.mpatric.mp3agic.UnsupportedTagException;

public class PlayerService extends Service {

	public static final String TAG = "PlayerService";
	
	public static final String BROADCAST_PREVIOUS = "net.airtheva.uniqueTask4.PREVIOUS";
	public static final String BROADCAST_TOGGLE = "net.airtheva.uniqueTask4.TOGGLE";
	public static final String BROADCAST_NEXT = "net.airtheva.uniqueTask4.NEXT";
	
	public static final String BROADCAST_ACTION_APPWIDGET_UPDATE = "net.airtheva.uniqueTask4.APPWIDGET_UPDATE";
	
	public static final String SHARED_PREFERENCES = "sharedPreferences";
	public static final String KEY_CURRENT_PLAYLIST_NAME = "currentPlaylistName";
	public static final String KEY_CURRENT_INDEX = "currentIndex";
	public static final String KEY_MODE = "mode";
	
	public static final int NOTIFICATION_ID = 0;
	
	public static final String PLAYLIST_NAME_DEFAULT = "default";
	public static final int INDEX_DEFAULT = 0;
	
	public static final int STATUS_UNKNOWN = -1;
	public static final int STATUS_PLAYING = 0;
	public static final int STATUS_PAUSING = 1;
	
	public static final int MODE_UNKNOWN = -1;
	public static final int MODE_SINGLE = 0;
	public static final int MODE_REPEAT_TRACK = 1;
	public static final int MODE_REPEAT_PLAYLIST = 2;
	public static final int MODE_RANDOM = 3;
	
	public static final int INTERVAL = 500;
	
	public class _BroadcastReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			String action = intent.getAction();
			
			Log.d(TAG, action);
			
			if(action.equals(BROADCAST_PREVIOUS)) {
				Previous();
			}
			else if(action.equals(BROADCAST_TOGGLE)) {
				Toggle();
			}
			else if(action.equals(BROADCAST_NEXT)) {
				Next();
			}
			
		}
		
	}
	
	class _Binder extends Binder {

		PlayerService GetServive() {
			return PlayerService.this;
		}
		
	}
	
	class _Ticker implements Runnable {

		@Override
		public void run() {
			// TODO Auto-generated method stub
			
			int size = mTicks.size();
			for(int i = 0; i < size; i++) {
				
				Runnable r = mTicks.get(i);
				r.run();
				
			}
			
			mHandler.postDelayed(this, INTERVAL);
			
		}
		
	}
	
	// TODO: Remove the 500ms Walker in Fragment, make register and unregister methods for the Walker below for convenient use.
	// For FloatLyricView.
	class Walker implements Runnable {

		@Override
		public void run() {
			// TODO Auto-generated method stub
			
			mFloatLyricView.SetTime(mMediaPlayer.getCurrentPosition());
			
			BroadcastAppWidgetUpdate();
				
		}
		
	}
	
	class _OnCompletionListener implements MediaPlayer.OnCompletionListener {

		@Override
		public void onCompletion(MediaPlayer mp) {
			// TODO Auto-generated method stub
			
			mIsPlaying = false;
			
			Next();
			
		}
		
	}
	
	_BroadcastReceiver mBroadcastReceiver;
	_Binder mBinder;
	
	NotificationManager mNotificationManager;
	
	Handler mHandler;
	_Ticker mTicker;
	ArrayList<Runnable> mTicks;
	
	MediaPlayer mMediaPlayer;
	boolean mIsPlaying = false;
	boolean mIsPaused = false;
	
	Playlist mCurrentPlaylist;
	int mCurrentIndex = -1;
	Playitem mCurrentPlayitem;
	int mMode = MODE_REPEAT_PLAYLIST;
	
	ControlNotification mControlNotification;
	FloatLyricView mFloatLyricView;
	
	Walker mWalker;
	
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		
		Log.d(TAG, "onCreate.");
		
		mBroadcastReceiver = new _BroadcastReceiver();
		mBinder = new _Binder();
		
		mHandler = new Handler();
		mTicker = new _Ticker();
		mTicks = new ArrayList<Runnable>();
		
		mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
		
		mMediaPlayer = new MediaPlayer();
		
		mControlNotification = new ControlNotification(this);
		
		mFloatLyricView = new FloatLyricView(this);
		mFloatLyricView.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				// FIXME: Cause Exception.
				//startActivity((new Intent(PlayerService.this, EntryActivity.class)).setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT));
			}
			
		});
		
		registerReceiver(mBroadcastReceiver, new IntentFilter(BROADCAST_PREVIOUS));
		registerReceiver(mBroadcastReceiver, new IntentFilter(BROADCAST_TOGGLE));
		registerReceiver(mBroadcastReceiver, new IntentFilter(BROADCAST_NEXT));

		SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
		
		Playlist playlist = new Playlist();
		playlist.Load(sharedPreferences.getString(KEY_CURRENT_PLAYLIST_NAME, PLAYLIST_NAME_DEFAULT));
		SetCurrentPlaylist(playlist);
		
		SetCurrentIndex(sharedPreferences.getInt(KEY_CURRENT_INDEX, INDEX_DEFAULT));
		SetMode(sharedPreferences.getInt(KEY_MODE, MODE_REPEAT_PLAYLIST));
		
		mWalker = new Walker();
		
		mTicks.add(mWalker);
		
		mHandler.postDelayed(mTicker, INTERVAL);
		
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.d(TAG, "onBind.");
		return mBinder;
	}
	
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		
		mHandler.removeCallbacks(mWalker);
		
		getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE).edit().putInt(KEY_MODE, mMode).commit();
		
		unregisterReceiver(mBroadcastReceiver);
		
		mMediaPlayer.stop();
		mMediaPlayer.release();
		
		super.onDestroy();
	}

	protected Playitem getPlayitem(Playlist playlist, int index) {
		return playlist.Playitems.get(index);
	}
	
	public void RegisterTick(Runnable r) {
		
		mTicks.add(r);
		
	}
	
	public void UnregisterTick(Runnable r) {
		
		mTicks.remove(r);
		
	}
	
	public void ShowNotification() {
		
		mControlNotification.SetArtist(mCurrentPlayitem.Artist);
		mControlNotification.SetTitle(mCurrentPlayitem.Title);
		mControlNotification.SetStatus(GetStatus());
		
		mNotificationManager.notify(NOTIFICATION_ID, mControlNotification.GetNotification());
		
	}
	
	public void Add(String uri) {
		
		try {
			Log.d(TAG, "Mp3File.");
			
			Playitem playitem = new Playitem();
			
			Mp3File f = new Mp3File(Uri.parse(uri).getPath());
			
			if(f.hasId3v1Tag()) {
				Log.d(TAG, "hasId3v1Tag.");
				ID3v1 t = f.getId3v1Tag();
				Log.d(TAG, t.getArtist());
				Log.d(TAG, t.getAlbum());
				Log.d(TAG, t.getTitle());
				
				playitem.Artist = t.getArtist();
				playitem.Album = t.getAlbum();
				playitem.Title = t.getTitle();
				
			}
			else if(f.hasId3v2Tag()) {
				Log.d(TAG, "hasId3v2Tag.");
				ID3v2 t = f.getId3v2Tag();
				Log.d(TAG, t.getArtist());
				Log.d(TAG, t.getAlbum());
				Log.d(TAG, t.getTitle());
				
				playitem.Artist = t.getArtist();
				playitem.Album = t.getAlbum();
				playitem.Title = t.getTitle();
				
			}
			
			mCurrentPlaylist.Add(playitem);
			mCurrentPlaylist.Save();
			
		} catch (UnsupportedTagException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void Previous() {
		
		int size = mCurrentPlaylist.Playitems.size();
		
		switch(mMode) {
		case MODE_SINGLE:
			break;
		case MODE_REPEAT_TRACK:
			
			Play(mCurrentPlaylist, mCurrentIndex);
			
			break;
		case MODE_REPEAT_PLAYLIST:
			
			if(mCurrentIndex - 1 > -1) {
				
				Play(mCurrentPlaylist, mCurrentIndex - 1);
				
			}
			else {
				
				Play(mCurrentPlaylist, size - 1);
				
			}
			
			break;
		case MODE_RANDOM:
			
			Random r = new Random();
			int randomIndex = (int) (r.nextFloat() * size);
			Play(mCurrentPlaylist, randomIndex);
			
			break;
		default:
			
			Log.d(TAG, "UNKNOWN_MODE");
			
			break;
		}
		
	}
	
	public void Play(Playlist playlist, int index) {
		
		try {
			
			if(mIsPlaying) { // Stop.
				mMediaPlayer.stop();
				//mMediaPlayer.release();
				
				mIsPlaying = false;
			}
			
			Playitem playitem = getPlayitem(playlist, index);
			
			//Add(playitem.Uri);
			
			mMediaPlayer.reset();

			// FIXME: Strange, can't put this in onCreate.
			mMediaPlayer.setOnCompletionListener(new _OnCompletionListener());
			
			mMediaPlayer.setDataSource(this, Uri.parse(playitem.Uri));
			mMediaPlayer.prepare();
			mMediaPlayer.start();
			
			mCurrentPlaylist = playlist;
			mCurrentIndex = index;
			mCurrentPlayitem = playitem;
			
			mIsPlaying = true;
			mIsPaused = false;
			
			ShowNotification();
			
			getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE).edit().putString("playlist", playlist.Name);
			
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void Toggle() {
		
		if(mIsPlaying) {
			
			if(mIsPaused) { // Resume.
				
				mMediaPlayer.start();
				
				mIsPaused = false;
				
			}
			else { // Pause.
				
				mMediaPlayer.pause();
				
				mIsPaused = true;
				
			}
			
		}
		
		ShowNotification();
		
	}
	
	public void Seek(int time) {
		
		mMediaPlayer.seekTo(time);
		
	}
	
	public void Next() {
		
		int size = mCurrentPlaylist.Playitems.size();
		
		switch(mMode) {
		case MODE_SINGLE:
			break;
		case MODE_REPEAT_TRACK:
			
			Play(mCurrentPlaylist, mCurrentIndex);
			
			break;
		case MODE_REPEAT_PLAYLIST:
			
			if(mCurrentIndex + 1 < size) {
				
				Play(mCurrentPlaylist, mCurrentIndex + 1);
				
			}
			else {
				
				Play(mCurrentPlaylist, 0);
				
			}
			
			break;
		case MODE_RANDOM:
			
			Random r = new Random();
			int randomIndex = (int) (r.nextFloat() * size);
			Play(mCurrentPlaylist, randomIndex);
			
			break;
		default:
			
			Log.d(TAG, "UNKNOWN_MODE");
			
			break;
		}
		
	}
	
	public void SetCurrentPlaylist(Playlist playlist) {
		
		mCurrentPlaylist = playlist;
		
	}
	
	public void SetCurrentIndex(int index) {
		
	}
	
	public void SetMode(int mode) {
		
		mMode = mode;
		
	}
	
	public void SetLyricParser(LyricParser lyricParser) {
		
		mFloatLyricView.SetLyricParser(lyricParser);
		
	}
	
	public Playlist GetCurrentPlaylist() {
		
		return mCurrentPlaylist;
		
	}
	
	public int GetCurrentIndex() {
		
		return mCurrentIndex;
		
	}
	
	public Playitem GetCurrentPlayitem() {
		
		return mCurrentPlayitem;
		
	}
	
	public int GetTotalTime() {
		
		return mMediaPlayer.getDuration();
		
	}
	
	public int GetCurrentTime() {
		
		return mMediaPlayer.getCurrentPosition();
		
	}
	
	public int GetStatus() {
		
		if(mIsPlaying) {
			
			if(mIsPaused) {
				return STATUS_PAUSING;
			}
			else {
				return STATUS_PLAYING;
			}
			
		}
		else {
			return STATUS_PAUSING;
		}
		
	}
	
	public int GetMode() {
		
		return mMode;
		
	}
	
	public boolean GetIsFloatLyricViewShowed() {
		return mFloatLyricView.GetIsShowed();
	}
	
	public void ShowFloatLyricView() {
		
		mFloatLyricView.Show();
		
	}
	
	public void HideFloatLyricView() {
		
		mFloatLyricView.Hide();
		
	}
	
	public void BroadcastAppWidgetUpdate() {
		
		Intent intent = new Intent();
		intent.setAction(BROADCAST_ACTION_APPWIDGET_UPDATE);
		
		if(mCurrentPlayitem != null) {
			intent.putExtra(ControlWidget.KEY_ARTIST, mCurrentPlayitem.Artist);
			intent.putExtra(ControlWidget.KEY_TITLE, mCurrentPlayitem.Title);
		}
		intent.putExtra(ControlWidget.KEY_STATUS, GetStatus());
				
		sendBroadcast(intent);
		
	}
	
}
